﻿using CsvHelper;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reactive;
using System.Reactive.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using TwinCAT.Ads;
using TwinCAT.Ads.Reactive;
using TwinCAT.Ads.TypeSystem;
using TwinCAT.TypeSystem;

namespace TwinCatAdsTest
{
    internal class Program
    {
        //static void Main(string[] args)
        //{
        //    using (TcAdsClient client = new TcAdsClient())
        //    {
        //        client.Connect("192.168.1.100.1.1",801);
        //        Symbol symbol = null;
        //        try
        //        {
        //            ISymbolLoader loader = SymbolLoaderFactory.Create(client, SymbolLoaderSettings.Default);
        //            // DINT Type (UINT32)
        //            symbol = (Symbol)loader.Symbols["MAIN.nCounter"];

        //            // Set the Notification Settings of the Symbol if NotificationSettings.Default is not appropriate
        //            // Check for change every 500 ms
        //            symbol.NotificationSettings = new NotificationSettings(AdsTransMode.OnChange, 500, 0);

        //            symbol.ValueChanged += Symbol_ValueChanged; // Registers the notification
        //            Thread.Sleep(500000); // Sleep the main thread to get some (asynchronous Notifications)
        //        }
        //        catch { }
        //        finally
        //        {
        //            // Unregister the Event and the underlying Handle
        //            symbol.ValueChanged -= Symbol_ValueChanged; // Unregisters the notification
        //        }
        //    }
        //}
        //private static void Symbol_ValueChanged(object sender, ValueChangedArgs e)
        //{
        //    Symbol symbol = (Symbol)e.Symbol;

        //    // Object Value can be cast to int automatically, because it is an Primitive Value (DINT --> Int32).
        //    // The Symbol information is used internally to cast the value to its appropriate .NET Type.
        //    int iVal = (int)e.Value;
        //    Console.WriteLine($"{DateTime.Now:HHmmss.fff}→{iVal}");
        //}

        //static void Main(string[] args)
        //{
        //    using (TcAdsClient client = new TcAdsClient())
        //    {
        //        // Add the Notification event 'Ex' handler
        //        client.AdsNotificationEx += Client_AdsNotification;

        //        // Connect to target
        //        client.Connect("192.168.1.100.1.1", 801);
        //        int notificationHandle = 0;
        //        try
        //        {
        //            // Notification to a ZDINT Type (UINT32)
        //            // Check for change every 200 ms
        //            notificationHandle = client.AddDeviceNotificationEx("MAIN.nCounter", AdsTransMode.OnChange, 200, 0, null, typeof(int));
        //            Thread.Sleep(500000); // Sleep the main thread to get some (asynchronous Notifications)
        //        }
        //        finally
        //        {
        //            // Unregister the Event / Handle
        //            client.DeleteDeviceNotification(notificationHandle);
        //            client.AdsNotificationEx -= Client_AdsNotification;
        //        }
        //    }
        //}
        //private static void Client_AdsNotification(object sender, AdsNotificationExEventArgs e)
        //{
        //    // Or here we know about UDINT type --> can be marshalled as UINT32
        //    int nCounter = (int)e.Value;
        //    Console.WriteLine($"{DateTime.Now:HHmmss.fff}→{nCounter}");
        //}

        //static AdsStream readStream = new AdsStream(sizeof(int));

        //static void Main(string[] args)
        //{
        //    using (TcAdsClient client = new TcAdsClient())
        //    {
        //        // Add the Notification event handler
        //        client.AdsNotification += Client_AdsNotification;

        //        // Connect to target
        //        client.Connect("192.168.1.100.1.1", 801);
        //        int notificationHandle = 0;

        //        try
        //        {
        //            // Notification to a DINT Type (UINT32)
        //            // Check for change every 200 ms
        //            notificationHandle = client.AddDeviceNotification("MAIN.nCounter", readStream, AdsTransMode.OnChange, 200, 0, null);
        //            Thread.Sleep(500000); // Sleep the main thread to get some (asynchronous Notifications)
        //        }
        //        finally
        //        {
        //            // Unregister the Event / Handle
        //            client.DeleteDeviceNotification(notificationHandle);
        //            client.AdsNotification -= Client_AdsNotification;
        //        }
        //    }
        //}
        //private static void Client_AdsNotification(object sender, AdsNotificationEventArgs e)
        //{
        //    int offset = (int)e.DataStream.Position;
        //    int length = (int)e.DataStream.Length;

        //    e.DataStream.Position = offset;
        //    AdsBinaryReader reader = new AdsBinaryReader(e.DataStream);

        //    // Read the Unmarshalled data
        //    //byte[] data = reader.ReadBytes(length);

        //    // Or here we know about UDINT type --> can be marshalled as UINT32
        //    int nCounter = reader.ReadInt32();
        //    Console.WriteLine($"{DateTime.Now:HHmmss.fff}→offset:{offset} length:{length} nCounter:{nCounter}");
        //}

        //static void Main(string[] args)
        //{
        //    using (TcAdsClient client = new TcAdsClient())
        //    {
        //        // Connect to target
        //        client.Connect("192.168.1.100.1.1", 801);

        //        // Create Symbol information
        //        var symbolLoader = SymbolLoaderFactory.Create(client, SymbolLoaderSettings.Default);
        //        IValueSymbol cycleCount = (IValueSymbol)symbolLoader.Symbols["MAIN.nCounter"];

        //        // Reactive Notification Handler
        //        var valueObserver = Observer.Create<object>(val =>
        //        {
        //            Console.WriteLine(string.Format("Instance: {0}, Value: {1}", cycleCount.InstancePath, val.ToString()));
        //        }
        //        );

        //        cycleCount.NotificationSettings = new NotificationSettings(AdsTransMode.OnChange, 200, 0); // optional: Change NotificationSettings on Symbol

        //        // Turning ADS Notifications into sequences of Value Objects (Taking 20 Values)
        //        // and subscribe to them.
        //        IDisposable subscription = cycleCount.WhenValueChanged().Take(20).Subscribe(valueObserver);

        //        Console.ReadKey(); // Wait for Key press
        //        subscription.Dispose(); // Dispose the Subscription
        //    }
        //}
        static void Main(string[] args)
        {
            using (TcAdsClient adsClient = new TcAdsClient())
            {
                // Add the Notification event 'Ex' handler
                adsClient.AdsNotificationEx += Client_AdsNotification;
                // Connect to target
                adsClient.Connect("192.168.1.100.1.1", 801);
                // ushort[]
                int handle4 = adsClient.CreateVariableHandle("MAIN.M1Points"); // ARRAY [0..47] OF M1Point
                int runspeed_x_handle = adsClient.CreateVariableHandle("MAIN.runspeed_x");
                int runspeed_y_handle = adsClient.CreateVariableHandle("MAIN.runspeed_y");
                int runspeed_z_handle = adsClient.CreateVariableHandle("MAIN.runspeed_z");
                M1Point[] m1Points;
                using (var reader = new StreamReader(Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, "Points.csv")))
                using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
                {
                    m1Points = csv.GetRecords<M1Point>().ToArray();
                }
                //M1Point[] m1Points = (M1Point[])adsClient.ReadAny(handle4, typeof(M1Point[]), new int[] { 48 });
                adsClient.WriteAny(handle4, m1Points);
                adsClient.WriteAny(runspeed_x_handle, 1000.0);
                adsClient.WriteAny(runspeed_y_handle, 1000.0);
                adsClient.WriteAny(runspeed_z_handle, 500.0);
                adsClient.DeleteVariableHandle(handle4);
                // Notification to a ZDINT Type (UINT32)
                // Check for change every 200 ms
                int notificationHandle = adsClient.AddDeviceNotificationEx("MAIN.cycleflag", AdsTransMode.OnChange, 100, 0, null, typeof(bool));
                Console.WriteLine("按任意键结束。。。");
                Console.ReadKey(); // Wait for Key press
                adsClient.DeleteDeviceNotification(notificationHandle);
                adsClient.AdsNotificationEx -= Client_AdsNotification;
            }
        }
        private static void Client_AdsNotification(object sender, AdsNotificationExEventArgs e)
        {
            // Or here we know about UDINT type --> can be marshalled as UINT32
            var cycleflag = (bool)e.Value;
            Console.WriteLine($"{DateTime.Now:HH:mm:ss.fff}→{cycleflag}");
        }
    }
    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public class M1Point
    {
        public double X { get; set; }
        public double Y { get; set; }
        public double Z { get; set; }
        public double Speed { get; set; }
        public ushort Type { get; set; }
    }
}
